// Includes
#include <PA9.h>       // Include for PA_Lib
#include "filelist.h"
#include "gfx/all_gfx.c"
#include "gfx/all_gfx.h"

char savetext[15][10]; // global for now
int recenttimes[13];

// Function: main()
int main(int argc, char ** argv)
{
	PA_Init();    // Initializes PA_Lib
	PA_InitVBL(); // Initializes a standard VBL
	AS_Init(AS_MODE_SURROUND | AS_MODE_16CH); //initialize audio
	AS_SetDefaultSettings(AS_PCM_8BIT, 11025, AS_SURROUND); // audio settings
	fatInitDefault(); // initialize the fat
	PA_SetVideoMode(1, 1);
			
	//initialize some variables and functions
	int timer;
	int inspect = 0; //the inspection time. 300=5seconds.
	bool timer_running = false;
	bool flip = false;
	void CreateMainMenu(int inspect, bool flip);
	void UpdateTimer(int timer, int inspect);
	bool CheckKeys(bool timer_running, int timer, int inspect);
	void Print_Scramble(char text[100]);
	void Scramble();
	void CheckOptions(int *inspect, bool *flip);
	void SaveTime(int timer, int inspect);
	
	CreateMainMenu(inspect, flip); // create the main menu
	Scramble(); // do the initial scramble
		
	// Infinite loop to keep the program running
	while (1)
	{
	   if (!timer_running) 
	   	CheckOptions(&inspect, &flip);  // if the timer isn't running, the user can change options; values are passed in as pointers so we can change multiple settings easily
	   timer = PA_VBLCounter[0]; // increases once per frame, 60 frames per second
	   if(timer <= inspect) //if inspection
	   {
	      if(timer==inspect-1)
		      PA_PlayPSG(8, 200, 127, 122, 4); // play a beep sound when inspection is over
		   UpdateTimer(timer, inspect); // update the timer display for inspection
		}	  
	   else //if not inspection
		{
		   if(timer==inspect+25)
		  		PA_PlayPSG(8, 0, 0, 0, 0); // stop the sound
			UpdateTimer(timer - inspect, 0); //update the timer display for timing after inspection
		}			
	   timer_running = CheckKeys(timer_running, timer, inspect); // check for keypresses to start/stop timer
		PA_WaitForVBL();  // updates the screen
	}	
	return 0;
} // End of main()

// Function: UpdateTimer
void UpdateTimer(int timer, int inspect)
{
   int minutes = timer/3600;
   timer = timer%3600;
   int seconds = timer/60;
   timer = timer%60;
   int ms = timer/.6;
   char text[100];
   
   if (inspect==900){
		sprintf(text, "-%02d:%02d.%02d", minutes, 14 - seconds, 99 - ms);
		if (timer==0) 	sprintf(text, "-%02d:%02d.%02d", 0, 15, 00); // if the timer is 0, make it look nice :)
	}		
   else if (inspect==600){
		sprintf(text, "-%02d:%02d.%02d", minutes, 9 - seconds, 99 - ms);
		if (timer==0) 	sprintf(text, "-%02d:%02d.%02d", 0, 10, 00); 
	}		
   else if (inspect==300){
		sprintf(text, "-%02d:%02d.%02d", minutes, 4 - seconds, 99 - ms);
		if (timer==0) 	sprintf(text, "-%02d:%02d.%02d", 0, 5, 00); 
	}		
   else
		sprintf(text, "%02d:%02d.%02d", minutes, seconds, ms);
   
	int i;
   for(i=0; i<16; i++){ // clear the area the timer prints on
		PA_Draw8bitLine(1,8*20,(8*9)+i,255,(8*9)+i,1); 
	}
	PA_SmartText(1, 8*20, 8*9, 255, 191, text, 5, 4, 1, 100);
}    

// Function: Print_Scramble()
void Print_Scramble(char text[100])
{
   PA_SmartText(1, 8*1, 8*1, 255-8, 8*5, text, 5, 3, 1, 100);
}   

// Function: Scramble()
void Scramble()
{
   char text[100] = "";
   int previousMove = 0;
   int currentMove = 0;
   int scrambleLength = 25;
   int modifier = 0;
   int movePart2 = 0;
   int i;
	for(i=0; i<scrambleLength; i++){
		currentMove = PA_RandMax(2);
		while (currentMove==previousMove) {
			currentMove = PA_RandMax(2);
		}
		modifier = PA_RandMax(2);
		movePart2 = PA_RandMax(2);
		if(movePart2==0){
			if(currentMove==0){
				strcat(text,"F");
			}
			if(currentMove==1){
				strcat(text,"R");
			}
			if(currentMove==2){
				strcat(text,"U");
			}
		}else{
			if(currentMove==0){
				strcat(text,"B");
			}
			if(currentMove==1){
				strcat(text,"L");
			}
			if(currentMove==2){
				strcat(text,"D");
			}
		}
		if(modifier==1){
			strcat(text,"'");
		}
		if(modifier==2){
			strcat(text,"2");
		}
		strcat(text," ");
		previousMove = currentMove;
	}
	Print_Scramble(text);
}

void CreateMainMenu(int inspect, bool flip)
{  
   PA_Init8bitBg(1, 3); // initialize top screen bg layer 3 as an 8-bit background, for nice text
	if (!flip) 
	{
	   PA_EasyBgLoad(1, 2, cuti_top); // load the top screen background, in bg layer 2			
	   PA_SetBgRot(1, 3, 0, 0, 0, 0, 0, 256);
	} 	
	else 
	{
	   PA_EasyBgLoad(1, 2, cuti_top_flip); // load the top screen background, in bg layer 2
  		PA_SetBgRot(1, 3, 255, 191, 0, 0, 256, 256);
 	} 		
	PA_SetBgPrio(1,3,2);
	PA_SetBgPrio(1,2,3);
	PA_InitText(1,0);
	PA_8bitCustomFont(5, font);
	
	PA_InitText(0,0);
	PA_EasyBgLoad(0, 2, cuti_options); // load the bottom screen background, in bg layer 2
	PA_SetTextCol(1,0,0,0);
	
	//some color palettes for the text
	PA_SetBgPalCol(1, 1, PA_RGB(31, 31, 31)); //white
	PA_SetBgPalCol(1, 2, PA_RGB(31, 0, 0)); //red
	PA_SetBgPalCol(1, 3, PA_RGB(0, 0, 31)); //blue
	PA_SetBgPalCol(1, 4, PA_RGB(0, 31, 0)); //green
	PA_SetBgPalCol(1, 5, PA_RGB(0, 0, 0)); //black
	
	//
	// create sprites
	//
	// inspection sprites
   PA_LoadSpritePal(0, 0, (void*)cuti_inspect0_Pal);
   PA_LoadSpritePal(0, 1, (void*)cuti_inspect5_Pal);
   PA_LoadSpritePal(0, 2, (void*)cuti_inspect10_Pal);
   PA_LoadSpritePal(0, 3, (void*)cuti_inspect15_Pal);
	PA_CreateSprite(0, 0, (void*)cuti_inspect0_Sprite, OBJ_SIZE_16X16, 1, 0, 8*11, 8);
	PA_CreateSprite(0, 1, (void*)cuti_inspect5_Sprite, OBJ_SIZE_16X16, 1, 1, (8*15)-4, 8);
	PA_CreateSprite(0, 2, (void*)cuti_inspect10_Sprite, OBJ_SIZE_16X16, 1, 2, 8*18, 8);
	PA_CreateSprite(0, 3, (void*)cuti_inspect15_Sprite, OBJ_SIZE_16X16, 1, 3, (8*22)-4, 8);
	
	//turn on the correct inspect button
	if (inspect==0) 	PA_SetSpriteAnim(0, 0, 1); // 'turn on' the inspect 0 button
	if (inspect==300) PA_SetSpriteAnim(0, 1, 1); // 'turn on' the inspect 5 button
	if (inspect==600) PA_SetSpriteAnim(0, 2, 1); // 'turn on' the inspect 10 button
	if (inspect==900) PA_SetSpriteAnim(0, 3, 1); // 'turn on' the inspect 15 button
	
	// algorithm button sprites
	PA_LoadSpritePal(0,4, (void*)cuti_alg1_Pal);
	PA_LoadSpritePal(0,5, (void*)cuti_alg2_Pal);
	PA_LoadSpritePal(0,6, (void*)cuti_alg3_Pal);
	PA_CreateSprite(0, 4, (void*)cuti_alg1_Sprite, OBJ_SIZE_64X32, 1, 4, 8*4, 8*18);
	PA_CreateSprite(0, 5, (void*)cuti_alg2_Sprite, OBJ_SIZE_64X32, 1, 5, 8*12, 8*18);
	PA_CreateSprite(0, 6, (void*)cuti_alg3_Sprite, OBJ_SIZE_64X32, 1, 6, 8*20, 8*18);
/*	
	// change puzzle button sprites
	PA_LoadSpritePal(0,7, (void*)cuti_change1_Pal);
	PA_LoadSpritePal(0,8, (void*)cuti_change2_Pal);
	PA_LoadSpritePal(0,9, (void*)cuti_change3_Pal);	
	PA_CreateSprite(0, 7, (void*)cuti_change1_Sprite, OBJ_SIZE_64X32, 1, 7, 8*4, 8*12);
	PA_CreateSprite(0, 8, (void*)cuti_change2_Sprite, OBJ_SIZE_64X32, 1, 8, 8*12, 8*12);
	PA_CreateSprite(0, 9, (void*)cuti_change3_Sprite, OBJ_SIZE_64X32, 1, 9, 8*20, 8*12);	
	// time editor button sprites
	PA_LoadSpritePal(0,10, (void*)cuti_timedit1_Pal);
	PA_LoadSpritePal(0,11, (void*)cuti_timedit2_Pal);
	PA_LoadSpritePal(0,12, (void*)cuti_timedit3_Pal);
	PA_CreateSprite(0, 10, (void*)cuti_timedit1_Sprite, OBJ_SIZE_64X32, 1, 10, 8*4, 8*6);
	PA_CreateSprite(0, 11, (void*)cuti_timedit2_Sprite, OBJ_SIZE_64X32, 1, 11, 8*12, 8*6);
	PA_CreateSprite(0, 12, (void*)cuti_timedit3_Sprite, OBJ_SIZE_64X32, 1, 12, 8*20, 8*6);	
*/	
	PA_LoadSpritePal(0,13, (void*)cuti_flip_Pal);
	PA_CreateSprite(0, 13, (void*)cuti_flip_Sprite, OBJ_SIZE_64X32, 1, 13, 256-48, 0);
	if (flip)
	{
	   PA_SetSpriteAnim(0, 13, 1); // turn on the flip button sprite  
	}   
}   


void SaveTime(timer, inspect)
{  
	if(timer <= inspect) return; //if still inspection, return without doing anything
	
	if(inspect==900) timer=timer-900;
   if(inspect==600) timer=timer-600;
   if(inspect==300) timer=timer-300;
   
   recenttimes[12] = recenttimes[11];
   recenttimes[11] = recenttimes[10];
   recenttimes[10] = recenttimes[9];
   recenttimes[9] = recenttimes[8];
   recenttimes[8] = recenttimes[7];
   recenttimes[7] = recenttimes[6];
   recenttimes[6] = recenttimes[5];
   recenttimes[5] = recenttimes[4];
   recenttimes[4] = recenttimes[3];
   recenttimes[3] = recenttimes[2];
   recenttimes[2] = recenttimes[1];
   recenttimes[1] = recenttimes[0];
   recenttimes[0] = timer;
/*   
	int avg3 = (recenttimes[0] + recenttimes[1] + recenttimes[2])/3; // avg of 3
   int avg5 = (recenttimes[0] + recenttimes[1] + recenttimes[2] + recenttimes[3] + recenttimes[4])/5; // avg of 5
   int avg10 = (recenttimes[0] + recenttimes[1] + recenttimes[2] + recenttimes[3] + recenttimes[4] + recenttimes[5] + recenttimes[6] + recenttimes[7] + recenttimes[8] + recenttimes[9])/10; // avg of 10
   */
   // convert the value to minutes and seconds and partial seconds
   int minutes = timer/3600;
   timer = timer%3600;
   int seconds = timer/60;
   timer = timer%60;
   int ms = timer/.6;
  
   //shift old times
   memcpy(savetext[11], savetext[10], 10);
   memcpy(savetext[10], savetext[9], 10);
   memcpy(savetext[9], savetext[8], 10);
   memcpy(savetext[8], savetext[7], 10);
   memcpy(savetext[7], savetext[6], 10);
   memcpy(savetext[6], savetext[5], 10);
   memcpy(savetext[5], savetext[4], 10);
   memcpy(savetext[4], savetext[3], 10);
   memcpy(savetext[3], savetext[2], 10);
   memcpy(savetext[2], savetext[1], 10);
   memcpy(savetext[1], savetext[0], 10);
   
   sprintf(savetext[0], "%02d:%02d.%02d", minutes, seconds, ms); // save the new time
	   
   //print last 12 times
   PA_SmartText(1, 8, 8*11, 255, 191, savetext[0], 5, 5, 1, 100);
   PA_SmartText(1, 8, 8*12, 255, 191, savetext[1], 5, 5, 1, 100);
   PA_SmartText(1, 8, 8*13, 255, 191, savetext[2], 5, 5, 1, 100);
   PA_SmartText(1, 8, 8*14, 255, 191, savetext[3], 5, 5, 1, 100);
   PA_SmartText(1, 8, 8*15, 255, 191, savetext[4], 5, 5, 1, 100);
	PA_SmartText(1, 8, 8*16, 255, 191, savetext[5], 5, 5, 1, 100);
	PA_SmartText(1, 8, 8*17, 255, 191, savetext[6], 5, 5, 1, 100);
	PA_SmartText(1, 8, 8*18, 255, 191, savetext[7], 5, 5, 1, 100);
	PA_SmartText(1, 8, 8*19, 255, 191, savetext[8], 5, 5, 1, 100);
	PA_SmartText(1, 8, 8*20, 255, 191, savetext[9], 5, 5, 1, 100);
	PA_SmartText(1, 8, 8*21, 255, 191, savetext[10], 5, 5, 1, 100);
	PA_SmartText(1, 8, 8*22, 255, 191, savetext[11], 5, 5, 1, 100);
/*
   // convert and print averages
   if (recenttimes[3]) {
   minutes = avg3/3600;
   avg3 = avg3%3600;
   seconds = avg3/60;
   avg3 = avg3%60;
   ms = avg3/.6;
   memset(savetext[12],0,10);
	sprintf(savetext[12], "Avg of  3: %02d:%02d.%02d", minutes, seconds, ms); // save the new time
	PA_SmartText(1, 8*10, 8*11, 255, 191, savetext[12], 5, 5, 1, 100);
	}	
	if (recenttimes[4]) {
   minutes = avg5/3600;
   avg5 = avg5%3600;
   seconds = avg5/60;
   avg5 = avg5%60;
   ms = avg5/.6;
   memset(savetext[13],0,10);
	sprintf(savetext[13], "Avg of  5: %02d:%02d.%02d", minutes, seconds, ms); // save the new time
	PA_SmartText(1, 8*10, 8*13, 255, 191, savetext[13], 5, 5, 1, 100);
	}	
	if(recenttimes[10]) {
   minutes = avg10/3600;
   avg10 = avg10%3600;
   seconds = avg10/60;
   avg10 = avg10%60;
   ms = avg10/.6;
   memset(savetext[14],0,10);
	sprintf(savetext[14], "Avg of 10: %02d:%02d.%02d", minutes, seconds, ms); // save the new time
	PA_SmartText(1, 8*10, 8*15, 255, 191, savetext[13], 5, 5, 1, 100);
	}		
	*/
}

// Function: CheckKeys
bool CheckKeys(bool timer_running, int timer, int inspect)
{
   if (timer_running)
   {
      if (Pad.Newpress.Anykey) // press any button to stop the timer
      {
         PA_VBLCounterPause(0); // pause the timer
         SaveTime(timer, inspect); // save the time
         while (Pad.Held.Anykey) // loop until all buttons are released, otherwise timer will restart when you release the button!
            PA_WaitForVBL();   
         
			int i;
         for(i=0; i<30; i++) // wait about 1/2 second so the user doesn't accidentally restart the timer. This probably needs some visual feedback added.
         {
         	PA_WaitForVBL();
       	}  	
         	
      	return false; // return that the timer stopped running
      }   
   }
   else // timer is not running
   {   
      if (Pad.Held.Anykey)
      {
         int i;
   		for(i=0; i<16; i++) // clear the area the timer prints on
				PA_Draw8bitLine(1,8*20,(8*9)+i,255,(8*9)+i,1); 
			PA_SmartText(1, 8*20, 8*9, 255, 191, "Ready...", 5, 4, 1, 100); // display a ready message
      }   
	   if (Pad.Released.Anykey && !Pad.Held.Anykey) // release a button while no other button is held to start the timer
	   {	
         PA_Clear8bitBg(1); // clear the whole top screen
         Scramble(); // generate a new scramble
      	PA_VBLCounterStart(0); // restart the counter from 0
      	return true; // return that the timer has started running
		} 
   }
   return timer_running; // if no input is received
}  

// Function: CheckOptions
void CheckOptions(int *inspect, bool *flip)
{
	if(PA_SpriteTouched(0)) // press inspect 0 button
	{
   	PA_SetSpriteAnim(0, 0, 1); // turn on the 0 button sprite
   	PA_SetSpriteAnim(0, 1, 0); // turn off the other sprites
   	PA_SetSpriteAnim(0, 2, 0); // turn off the other sprites
   	PA_SetSpriteAnim(0, 3, 0); // turn off the other sprites
   	*inspect = 0;
   	PA_VBLCounterStart(0); PA_VBLCounterPause(0); //reset timer to 0;
	}

	else if(PA_SpriteTouched(1)) // press inspect 5 button
	{
   	PA_SetSpriteAnim(0, 0, 0); 
   	PA_SetSpriteAnim(0, 1, 1); 
   	PA_SetSpriteAnim(0, 2, 0); 
   	PA_SetSpriteAnim(0, 3, 0); 
   	*inspect = 300;
   	PA_VBLCounterStart(0); PA_VBLCounterPause(0); //reset timer to 0;
	}

	else if(PA_SpriteTouched(2)) // press inspect 10 button
	{
   	PA_SetSpriteAnim(0, 0, 0);
   	PA_SetSpriteAnim(0, 1, 0); 
   	PA_SetSpriteAnim(0, 2, 1); 
   	PA_SetSpriteAnim(0, 3, 0); 
   	*inspect = 600;
   	PA_VBLCounterStart(0); PA_VBLCounterPause(0); //reset timer to 0;
	}

	else if(PA_SpriteTouched(3)) // press inspect 15 button
	{
   	PA_SetSpriteAnim(0, 0, 0); 
   	PA_SetSpriteAnim(0, 1, 0); 
   	PA_SetSpriteAnim(0, 2, 0); 
   	PA_SetSpriteAnim(0, 3, 1); 
   	*inspect = 900;
   	PA_VBLCounterStart(0); PA_VBLCounterPause(0); //reset timer to 0;
	}
	
	if(PA_SpriteTouched(13)) // press flip button
	{
	   if (Stylus.Newpress)
	   {
		   if (*flip == false)
		   {
	   		PA_SetSpriteAnim(0, 13, 1); // turn on the flip button sprite
	   		*flip = true;
	   		PA_SetBgRot(1, 3, 255, 191, 0, 0, 256, 256);
				PA_EasyBgLoad(1, 2, cuti_top_flip); // load the top screen background, in bg layer 2	
				PA_SetBgPrio(1,2,3);	
	  		} 		
	   	else
	   	{
	   	 	PA_SetSpriteAnim(0, 13, 0); // turn off the flip button sprite
	   		*flip = false;  
	   		PA_SetBgRot(1, 3, 0, 0, 0, 0, 0, 256);
				PA_EasyBgLoad(1, 2, cuti_top); // load the top screen background, in bg layer 2
				PA_SetBgPrio(1,2,3);
	   	}   
		   while (Stylus.Held) PA_WaitForVBL();	
		}   
	}
	
	// make sure algorithm sprites are not highlighted
	PA_SetSpriteAnim(0, 4, 0); 
	PA_SetSpriteAnim(0, 5, 0); 
	PA_SetSpriteAnim(0, 6, 0); 
	if(PA_SpriteTouched(4) || PA_SpriteTouched(5) || PA_SpriteTouched(6)) // press algorithm button
	{
	   PA_SetSpriteAnim(0, 4, 1); 
	   PA_SetSpriteAnim(0, 5, 1); 
	   PA_SetSpriteAnim(0, 6, 1); 
	   if(Stylus.Newpress)
	   {
	      PA_ResetBgSys();
			PA_ResetSpriteSys();
	      createlistbox(); // load algorithm viewer
	      
      	PA_ResetBgSys();
			PA_ResetSpriteSys();
			CreateMainMenu(*inspect, *flip);
			Scramble();
			int i;   
         for(i=0; i<30; i++) // wait about 1/2 second so the user doesn't accidentally restart the timer. This probably needs some visual feedback added.
         {
         	PA_WaitForVBL();
       	}  
	   }   
	}
/*
	// make sure change puzzle sprites are not highlighted
	PA_SetSpriteAnim(0, 7, 0); 
	PA_SetSpriteAnim(0, 8, 0); 
	PA_SetSpriteAnim(0, 9, 0); 
	if(PA_SpriteTouched(7) || PA_SpriteTouched(8) || PA_SpriteTouched(9)) // press change puzzle button
	{
	   PA_SetSpriteAnim(0, 7, 1); 
	   PA_SetSpriteAnim(0, 8, 1); 
	   PA_SetSpriteAnim(0, 9, 1); 
	   if(Stylus.Newpress)
	   {
	      PA_Print(0,"PUZ");
	   }   
	}	

   // make sure time editor sprites are not highlighted
	PA_SetSpriteAnim(0, 10, 0); 
	PA_SetSpriteAnim(0, 11, 0); 
	PA_SetSpriteAnim(0, 12, 0); 
	if(PA_SpriteTouched(10) || PA_SpriteTouched(11) || PA_SpriteTouched(12)) // press algorithm button
	{
	   PA_SetSpriteAnim(0, 10, 1); 
	   PA_SetSpriteAnim(0, 11, 1); 
	   PA_SetSpriteAnim(0, 12, 1); 
	   if(Stylus.Newpress)
	   {
	      PA_Print(0,"TIM");
	   }   
	}
*/
}   
